/*
 * PacketReceiver.h
 *
 *  Created on: Dec 22, 2016
 *      Author: root
 */

#ifndef PACKETRECEIVER_H_
#define PACKETRECEIVER_H_

#include "Poco/Exception.h"
#include <Poco/Net/NetException.h>
#include <Poco/Types.h>
#include <Poco/Net/StreamSocket.h>
#include <iostream>

using namespace Poco;
using namespace Poco::Net;

#include "Packet.h"

namespace util
{

class PacketReceiver
{
public:
	PacketReceiver(StreamSocket& socket, int nMSTimeout = 0)
	: _socket(socket)
	, _packet()
	, _readLen(0)
	, _timeout(nMSTimeout) {}

	Packet* recv()
	{
		if (_packet.getData() == nullptr)
		{
			const PacketHeader *pheader = _packet.getHeader();
			//int n = socket.receiveBytes(, 4 - _readLen, 0);
			_readLen += recv((char *)&(((char *)pheader)[_readLen]), sizeof(PacketHeader) - _readLen);
			std::cout << "received packet header length: " << _readLen << std::endl;
			if (_readLen == sizeof(PacketHeader))
			{
				std::string errmsg;
				int err = _packet.checkHeader(pheader, errmsg);
				if (err < 0)
				{
					std::cout << errmsg << std::endl;
					throw Poco::Net::MessageException(errmsg);
				}

				_packet.allocData();
			}
			else
			{
				return nullptr;
			}
		}

		_readLen += recv((char *)&_packet.getData()[_readLen - sizeof(PacketHeader)], _packet.getHeader()->length - sizeof(PacketHeader));
		if (_readLen == _packet.getHeader()->length)
		{
			_readLen = 0;

			return new Packet(_packet);
		}

		return nullptr;
	}
private:
	int recv(char *buff, int size)
	{
		int n = 0;
		Timestamp tsStartRecv;

		n = _socket.receiveBytes(buff, size, 0);
		if (n == 0)
		{
			std::string str = "peer socket closed";
			std::cout << str << std::endl;
			throw Poco::Net::NetException(str);
		}
		else if (n < 0)
		{
			Thread::sleep(100);
			if (_timeout > 0 && tsStartRecv.isElapsed(_timeout))
			{
				std::string str = "socket receiving timeout.";
				std::cout << str << std::endl;
				throw Poco::TimeoutException(str);
			}
//				std::string str = "socket error occurred when receiving, return code = " + std::to_string(n);
//				std::cout << str << std::endl;
//				throw Poco::Net::NetException(str);
		}
		else
		{
			//std::cout << "ddddddddddddddddddddd: length = " << n << std::endl;
		}

		return n;
	}

	StreamSocket _socket;
	Packet _packet;
	UInt32 _readLen = 0;
	int _timeout;
};

} // endof namespace util

#endif /* PACKETRECEIVER_H_ */
